// [MathPresso]
// Mathematical Expression Parser and JIT Compiler.
//
// [License]
// Zlib - See LICENSE.md file in the package.

// [Guard]
#ifndef _MATHPRESSO_H
#define _MATHPRESSO_H

#include <stdint.h>
#include <stdlib.h>

namespace mathpresso {

// MathPresso Configuration
// ========================

// DEPRECATED: Will be removed in the future.
#if defined(MATHPRESSO_BUILD_EMBED) || defined(MATHPRESSO_BUILD_STATIC)
  #if defined(MATHPRESSO_BUILD_EMBED)
    #pragma message("'MATHPRESSO_BUILD_EMBED' is deprecated, use 'MATHPRESSO_STATIC'")
  #endif
  #if defined(MATHPRESSO_BUILD_STATIC)
    #pragma message("'MATHPRESSO_BUILD_STATIC' is deprecated, use 'MATHPRESSO_STATIC'")
  #endif

  #if !defined(MATHPRESSO_STATIC)
    #define MATHPRESSO_STATIC
  #endif
#endif

// MathPresso Definitions
// ======================

//! \def MATHPRESSO_API
//!
//! MathPresso API decorator.
#if !defined(MATHPRESSO_STATIC)
  #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
    #if defined(MATHPRESSO_BUILD_EXPORT)
      #define MATHPRESSO_API __declspec(dllexport)
    #else
      #define MATHPRESSO_API __declspec(dllimport)
    #endif
  #elif defined(_WIN32) && defined(__GNUC__)
    #if defined(MATHPRESSO_BUILD_EXPORT)
      #define MATHPRESSO_API __attribute__((dllexport))
    #else
      #define MATHPRESSO_API __attribute__((dllimport))
    #endif
  #elif defined(__GNUC__)
    #define MATHPRESSO_API __attribute__((__visibility__("default")))
  #endif
#endif

#ifndef MATHPRESSO_API
  #define MATHPRESSO_API
#endif

//! \def MATHPRESSO_NOAPI
//!
//! MathPresso hidden API decorator.
#define MATHPRESSO_NOAPI

//! \def MATHPRESSO_INLINE
//!
//! MathPresso inline decorator.
#if defined(__clang__)
  #define MATHPRESSO_INLINE inline __attribute__((__always_inline__, __visibility__("hidden")))
#elif defined(__GNUC__)
  #define MATHPRESSO_INLINE inline __attribute__((__always_inline__))
#elif defined(_MSC_VER)
  #define MATHPRESSO_INLINE __forceinline
#else
  #define MATHPRESSO_INLINE inline
#endif

#define MATHPRESSO_NONCOPYABLE(type) \
private: \
  MATHPRESSO_INLINE type(const type& other); \
  MATHPRESSO_INLINE type& operator=(const type& other); \
public:

//! Get an offset of `field` in a struct `type`.
#define MATHPRESSO_OFFSET(type, field) \
  ((int)(size_t) ((const char*) &((const type*)0x10)->field) - 0x10)

#define MATHPRESSO_ARRAY_SIZE(array) \
  (sizeof(array) / sizeof(array[0]))

// Forward Declarations
// ====================

struct OutputLog;
struct Expression;

// MathPresso Typedefs
// ===================

//! MathPresso result type (signed integer).
typedef unsigned int Error;

//! Prototype of the compiled function generated by MathPresso.
typedef void (*CompiledFunc)(double* result, void* data);

typedef double (*Arg0Func)(void);
typedef double (*Arg1Func)(double);
typedef double (*Arg2Func)(double, double);
typedef double (*Arg3Func)(double, double, double);
typedef double (*Arg4Func)(double, double, double, double);
typedef double (*Arg5Func)(double, double, double, double, double);
typedef double (*Arg6Func)(double, double, double, double, double, double);
typedef double (*Arg7Func)(double, double, double, double, double, double, double);
typedef double (*Arg8Func)(double, double, double, double, double, double, double, double);

// MathPresso Error Codes
// ======================

//! MathPresso error codes.
enum ErrorCode {
  //! No error.
  kErrorOk = 0,
  //! No memory.
  kErrorNoMemory = 1,
  //! Invalid argument.
  kErrorInvalidArgument,
  //! Invalid state.
  kErrorInvalidState,

  //! No expression was given.
  kErrorNoExpression,
  //! Invalid syntax.
  kErrorInvalidSyntax,

  //! Symbol not found.
  kErrorSymbolNotFound,
  //! Symbol already exists.
  kErrorSymbolAlreadyExists
};


// MathPresso Options
// ==================

//! MathPresso options.
enum Options {
  //! No options.
  kNoOptions = 0x00000000u,

  //! Show messages and warnings.
  kOptionVerbose = 0x0001u,
  //! Debug AST (shows initial and final AST).
  kOptionDebugAst = 0x0002u,
  //! Debug machine code generated.
  kOptionDebugMachineCode = 0x0004u,
  //! Debug AsmJit's compiler.
  kOptionDebugCompiler = 0x0008u,

  //! Do not use SSE4.1 instruction set even if CPU supports it.
  //!
  //! \note This is used during testing to ensure that all code-paths produce
  //! the same results regardless of the highest instruction set used. Since
  //! SSE4.1 is the most beneficial instruction set for MathPresso there is
  //! only this option (MathPresso doesn't use SSE3 and SSSE3 at the moment).
  kOptionDisableSSE4_1 = 0x4000u,

  //! \internal
  //!
  //! Mask of all accessible options, MathPresso uses also \ref InternalOptions
  //! that should not collide with \ref Options.
  _kOptionsMask = 0xFFFFu
};

// MathPresso Variable Flags
// =========================

//! Variable flags.
enum VariableFlags {
  kVariableRW = 0x00000000u,
  kVariableRO = 0x00000001u
};

// MathPresso Function Flags
// =========================

enum FunctionFlags {
  //! Function has 0 arguments.
  kFunctionArg0 = 0x00000000u,
  //! Function has 1 argument.
  kFunctionArg1 = 0x00000001u,
  //! Function has 2 arguments.
  kFunctionArg2 = 0x00000002u,
  //! Function has 3 arguments.
  kFunctionArg3 = 0x00000003u,
  //! Function has 4 arguments.
  kFunctionArg4 = 0x00000004u,
  //! Function has 5 arguments.
  kFunctionArg5 = 0x00000005u,
  //! Function has 6 arguments.
  kFunctionArg6 = 0x00000006u,
  //! Function has 7 arguments.
  kFunctionArg7 = 0x00000007u,
  //! Function has 8 arguments.
  kFunctionArg8 = 0x00000008u,

  //! \internal
  _kFunctionArgMask = 0x0000000Fu,

  //! The first argument of the function is the `data` pointer passed to the
  //! evaluate function. This is a hidden parameter that is not accessible
  //! within the expression itself.
  kFunctionFirstArgData = 0x10000000u,

  //! Function doesn't have side-effects and can be evaluated (i.e. optimized
  //! out) during a constant folding phase.
  kFunctionNoSideEffects = 0x80000000u
};

// MathPresso Context
// ==================

struct ContextImpl {
  //! Reference count (atomic).
  uintptr_t _refCount;
};
//! MathPresso context.
//!
//! Context is an environment where you can add/remove constants, variables and
//! functions. Context is a reference-counted class that is using copy-on-write.
//! Working with context is reentrant and making weak or deep copy is thread-safe
//! (reference counting is atomic). It is possible to create one master context
//! and use it from different threads to compile many expressions.
struct Context {
  // Members
  // -------

  //! Private data not available to the MathPresso public API.
  ContextImpl* _d;

  // Construction & Destruction
  // --------------------------

  //! Create a new `Context` instance.
  MATHPRESSO_API Context();
  //! Create a new `Context` based on `other`.
  MATHPRESSO_API Context(const Context& other);
  //! Destroy the `Context` instance.
  MATHPRESSO_API ~Context();

  // Copy & Reset
  // ------------

  //! Delete all symbols.
  MATHPRESSO_API Error reset();
  //! Assignement operator.
  MATHPRESSO_API Context& operator=(const Context& other);

  // Interface
  // ---------

  //! Add built-in intrinsics and constants.
  MATHPRESSO_API Error addBuiltIns(void);

  //! Add constant to this context.
  MATHPRESSO_API Error addConstant(const char* name, double value);
  //! Add variable to this context.
  MATHPRESSO_API Error addVariable(const char* name, int offset, unsigned int flags = kVariableRW);
  //! Add function to this context.
  MATHPRESSO_API Error addFunction(const char* name, void* fn, unsigned int flags);

  //! Delete symbol from this context.
  MATHPRESSO_API Error delSymbol(const char* name);
};

// MathPresso Expresion
// ====================

//! MathPresso expression.
struct Expression {
  MATHPRESSO_NONCOPYABLE(Expression)

  // Members
  // -------

  //! Compiled function.
  CompiledFunc _func;

  // Construction & Destruction
  // --------------------------

  //! Create a new `Expression` instance.
  MATHPRESSO_API Expression();
  //! Destroy the `Expression` instance.
  MATHPRESSO_API ~Expression();

  // Interface
  // ---------

  //! Parse and compile a given expression.
  //!
  //! \param ctx MathPresso's \ref Context to use.
  //! \param body Expression to parse and compile.
  //! \param options MathPresso options (flags), see \ref Options.
  //! \param log Used to catch messages a parser, optimizer, and compiler may
  //!        generate
  //!
  //! Returns MathPresso's error code, see \ref Error.
  MATHPRESSO_API Error compile(const Context& ctx, const char* body, unsigned int options, OutputLog* log = NULL);

  //! Get whether the `Expression` contains a valid compiled expression.
  MATHPRESSO_API bool isCompiled() const;

  //! Reset the expression.
  MATHPRESSO_API void reset();

  //! Evaluate expression with variable substitutions.
  //!
  //! Returns the result of the evaluated expression, NaN otherwise.
  MATHPRESSO_INLINE double evaluate(void* data) const {
    double result;
    _func(&result, data);
    return result;
  }
};

// MathPresso OutputLog
// ====================

//! Interface that can be used to catch compiler warnings and errors.
struct MATHPRESSO_API OutputLog {
  //! Output type.
  //!
  //! Specifies how much information to return after a program is parsed/compiled.
  enum Message {
    //! Error message.
    kMessageError = 0,
    //! warning.
    kMessageWarning,
    //! AST initial.
    kMessageAstInitial,
    //! AST final.
    kMessageAstFinal,
    //! Machine code.
    kMessageAsm
  };

  // Construction & Destruction
  // --------------------------

  OutputLog();
  virtual ~OutputLog();

  // Interface
  // ---------

  virtual void log(unsigned int type, unsigned int line, unsigned int column, const char* message, size_t size) = 0;
};

} // {mathpresso}

#endif // _MATHPRESSO_H
